using System;
using FizzWare.NBuilder.Dates;
using NUnit.Framework;

namespace NAsserter.Tests
{
    public class AssertThat_Between : AssertionHelper
    {
        [Test]
        public void with_nullable_int_value()
        {
            AssertThat.Argument((int?)null, "age")
                .Between(10).And(20);
        }

        [Test]
        public void arg__with_nullable_int_value()
        {
            AssertThat.Argument((int?)null, "age")
                .Between(10).And(20);
        }

        [Test]
        public void with_nullable_datetime_value()
        {
            AssertThat
                .Argument((DateTime?)null, "birthday")
                    .Between(On.January.The5th)
                        .And(On.January.The10th);
        }

        [Test]
        public void with_nullable_object_value()
        {
            var startEntity = new TestEntity {Id = 5};
            var endEntity = new TestEntity {Id = 10};

            AssertThat
                .Argument((TestEntity)null, "entity")
                    .Between(startEntity).And(endEntity);
        }

        [Test]
        public void with_nullable_object_in_range()
        {
            var startEntity = new TestEntity { Id = 5 };
            var endEntity = new TestEntity { Id = 10 };
            var arg = new TestEntity { Id = 5 };

            AssertThat
                .Argument(arg, "entity")
                    .Between(startEntity)
                        .And(endEntity);
        }

        [Test]
        public void with_int_in_range()
        {
            AssertThat.Argument(11, "age")
                .Between(10).And(20);
        }

        [Test]
        public void with_int_out_of_range()
        {
            Expect(
                () => AssertThat
                        .Argument(9, "age")
                            .Between(10).And(20),
                Throws.TypeOf<ArgumentOutOfRangeException>().With.Property("ParamName").EqualTo("age"));
        }

        [Test]
        public void arg__with_decimal_out_of_range()
        {
            var entity = new TestEntity {Amount = 9.099m};

            Expect(
                () => AssertThat
                    .Argument(entity, "entity")
                        .Property(x => x.Amount, "Amount")
                        .Assertions(x => x.Between(9.1m).And(9.8m)),
                Throws.TypeOf(typeof(ArgumentOutOfRangeException)));
        }

        [Test]
        public void arg__with_decimal_in_range()
        {
            var entity = new TestEntity { Amount = 9.099m };

            AssertThat
                .Argument(entity, "entity")
                    .Property(x => x.Amount, "Amount").Assertions(x => x.Between(9.01m).And(9.8m));
        }

        [Test]
        public void arg__with_object_out_of_range()
        {
            var fromEntity = new TestEntity {Id = 5};
            var toEntity = new TestEntity {Id = 10};

            var entity = new TestEntity {Id = 4};

            Expect(
                () => AssertThat
                    .Argument(entity, "entity")
                        .Between(fromEntity).And(toEntity),
                Throws.TypeOf(typeof(ArgumentOutOfRangeException))
                    .With.Message.Contains("entity")
                    .And.Property("ParamName").Contains("entity"));
        }
    }

    public class TestEntity : IComparable<TestEntity>
    {
        public int Id { get; set; }

        public decimal Amount { get; set; }

        public TestEntity Parent { get; set; }

        public int CompareTo(TestEntity other)
        {
            if (other == null) throw new ArgumentNullException("other");

            return Id - other.Id;
        }
    }
}